// Copyright © 2021 NAME HERE <EMAIL ADDRESS>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"bytes"
	hdmap "camap/modules/map/proto"
	"camap/pkg/apollo"
	"camap/pkg/fabu"
	"database/sql"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"time"

	"github.com/shiena/ansicolor"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"google.golang.org/protobuf/proto"

	_ "github.com/shaxbee/go-spatialite"
)

var (
	xyz, fast bool
)

// makeCmd represents the new command
var makeCmd = &cobra.Command{
	Use:   "make",
	Short: "make is the command to make apollo hdmap & fabu hdmap",
	Long:  `A longer description.`,
	Args:  cobra.MinimumNArgs(1),
	Run: func(cmd *cobra.Command, args []string) {

		st := time.Now()
		w := ansicolor.NewAnsiColorWriter(os.Stdout)

		srid := viper.GetInt("hdmap.srid")
		tSrid := viper.GetInt("hdmap.targetSrid")

		for _, arg := range args {
			sst := time.Now()
			_, err := os.Stat(arg)
			if err != nil {
				fmt.Fprintf(w, "%s hdmap file(%s) stat error, details:%v\n", "\x1b[35m", arg, err)
				log.Errorf("hdmap file(%s) stat error, details: %v", err)
				continue
			}
			db, err := sql.Open("spatialite", arg)
			if err != nil {
				fmt.Fprintf(w, "%s open hdmap file(%s) failed, details:%v\n", "\x1b[35m", arg, err)
				log.Errorf("open hdmap file(%s) failed, details: %v", err)
				continue
			}
			defer db.Close()

			//初始化apollo模块的数据库驱动
			apollo.SetDB(db)
			//xyz为true则输出z值
			if xyz {
				apollo.SetXYZ(xyz)
			}

			row := db.QueryRow(`SELECT PROJ_AsProjString(null,?);`, tSrid)
			var projStr string
			if row.Scan(&projStr); err != nil {
				fmt.Fprintf(w, "%sget projStr(%d) failed, details:%v\n", "\x1b[35m", tSrid, err)
				continue
			}
			proj := hdmap.Projection{
				Proj: &projStr,
			}
			//获取数据范围,使用道路参考线范围
			extentLayer := viper.GetString("hdmap.extentLayer")
			l, b, r, t := apollo.GetLayerExtent(extentLayer, srid, 4490)
			version := viper.GetString("hdmap.version")
			head := hdmap.Header{
				Version:    []byte(version),
				Projection: &proj,
				Date:       []byte(time.Now().String()),
				District:   []byte("0"),
				RevMajor:   []byte("1"),
				RevMinor:   []byte("3"),
				Left:       &l,
				Top:        &t,
				Right:      &r,
				Bottom:     &b,
				Vendor:     []byte("CAIC@DMAP-AODX"),
			}
			fmt.Fprintf(w, "%smake hdmap header completed, lbrt:(%f,%f,%f,%f), target projStr:%s\n", "\x1b[32m", l, b, r, t, projStr)
			sst = time.Now()
			roads := apollo.MakeRoads(fast)
			log.Infof("make all roads, time cost: %fs ~\n", time.Since(sst).Seconds())
			sst = time.Now()
			lanes := apollo.MakeLanes()
			log.Infof("make all lanes, time cost: %fs ~\n", time.Since(sst).Seconds())
			sst = time.Now()
			junctions := apollo.MakeJunctions()
			log.Infof("make all junctions, time cost: %fs ~\n", time.Since(sst).Seconds())
			sst = time.Now()
			crosswalks := apollo.MakeCrosswalks()
			log.Infof("make all crosswalks, time cost: %fs ~\n", time.Since(sst).Seconds())
			sst = time.Now()
			signals := apollo.MakeSignals()
			log.Infof("make all signals, time cost: %fs ~\n", time.Since(sst).Seconds())
			sst = time.Now()
			overlaps := apollo.MakeAllOverlaps()
			log.Infof("make all overlaps, time cost: %fs ~\n", time.Since(sst).Seconds())
			sst = time.Now()
			pncJunctions := apollo.MakePncJunctions()
			log.Infof("make all pncJunctions, time cost: %fs ~\n", time.Since(sst).Seconds())
			//stop_sign是停止标志和stop_line还不同（正常理解stopsign应该包含了停止线）
			//在此暂不输出，stopline已经在signal中输出了
			sst = time.Now()
			hmap := hdmap.Map{
				Header:      &head,
				Road:        roads,
				Lane:        lanes,
				Junction:    junctions,
				Crosswalk:   crosswalks,
				Signal:      signals,
				Overlap:     overlaps,
				PncJunction: pncJunctions,
			}
			log.Infof("make all hd map, time cost: %fs ~\n", time.Since(sst).Seconds())

			sst = time.Now()
			data, err := proto.Marshal(&hmap)
			if err != nil {
				log.Fatalf("地图编码失败, details: %v", err)
			}
			log.Infof("marshal hd map, time cost: %fs ~\n", time.Since(sst).Seconds())
			//把数据写入文件
			ioutil.WriteFile("./base_map.bin", data, os.ModePerm)
			sst = time.Now()
			cmd := exec.Command("cmd.exe", "/c", "protoc.exe", "--decode=apollo.hdmap.Map", "modules/map/proto/map.proto", "--proto_path=../")
			cmd.Stdin = bytes.NewReader(data)
			var out bytes.Buffer
			cmd.Stdout = &out
			if err := cmd.Run(); err != nil {
				fmt.Println("转换 c++ format Error: ", err)
			}
			ioutil.WriteFile("./base_map.txt", out.Bytes(), os.ModePerm)
			fmt.Printf("base_map.txt output finished~\n")
			fmt.Printf("covert to cpp txt time： %fs ~\n", time.Since(sst).Seconds())
			sst = time.Now()
			outxml := fabu.MakeFabuXML(&hmap)
			fmt.Printf("make fabu xml, time cost： %fs ~\n", time.Since(sst).Seconds())
			ioutil.WriteFile("./base_map.xml", outxml, os.ModePerm)
			fmt.Printf("base_map.xml output finished~\n")
			fmt.Printf("TIME COST： %fs ~\n", time.Since(st).Seconds())
		}
		fmt.Fprintf(w, "%s", "\x1b[37m") //恢复命令行颜色
	},
}

func init() {
	rootCmd.AddCommand(makeCmd)
	makeCmd.Flags().BoolVar(&xyz, "xyz", false, "set z value ~")
	makeCmd.Flags().BoolVar(&fast, "fast", false, "enabel fast routine ~")
}
